package com.yunyan.util.predict;

import org.apache.commons.math3.linear.Array2DRowRealMatrix;
import org.apache.commons.math3.linear.MatrixUtils;
import org.apache.commons.math3.linear.RealMatrix;

import java.util.ArrayList;
import java.util.List;

public class ValuePrediction {
    public static int predict(int[] X0) {
        //int[] X0 = {680, 670, 655};
        List<Integer> X1 = new ArrayList<>();
        X1.add(X0[0]);
        int add = X0[0] + X0[1];
        X1.add(add);

        for (int i = 2; i < X0.length; i++) {
            add += X0[i];
            X1.add(add);
        }

        List<Double> Z = new ArrayList<>();
        for (int j = 1; j < X1.size(); j++) {
            double num = (X1.get(j) + X1.get(j - 1)) / 2.0;
            Z.add(num);
        }

        List<Double> Y = new ArrayList<>();
        for (int x_i = 1; x_i < X0.length; x_i++) {
            Y.add((double) X0[x_i]);
        }

        double[][] BData = new double[Z.size()][2];
        for (int b = 0; b < Z.size(); b++) {
            BData[b][0] = -Z.get(b);
            BData[b][1] = 1;
        }
        RealMatrix B = new Array2DRowRealMatrix(BData);

        double[][] YData = new double[Y.size()][1];
        for (int y = 0; y < Y.size(); y++) {
            YData[y][0] = Y.get(y);
        }
        RealMatrix YMatrix = new Array2DRowRealMatrix(YData);

        RealMatrix BTB = B.transpose().multiply(B);
        RealMatrix BTBInverse = MatrixUtils.inverse(BTB);
        RealMatrix alpha = BTBInverse.multiply(B.transpose()).multiply(YMatrix);

        double a = alpha.getEntry(0, 0);
        double b = alpha.getEntry(1, 0);

        System.out.println("a=" + a);
        System.out.println("b=" + b);

        List<Double> GM = new ArrayList<>();
        GM.add((double) X0[0]);
        double did = b / a;
        for (int k = 1; k < X0.length; k++) {
            GM.add((X0[0] - did) * Math.exp(-a * k) + did);
        }

        List<Integer> G = new ArrayList<>();
        G.add(X0[0]);
        for (int g = 1; g < X0.length; g++) {
            G.add((int) Math.round(GM.get(g) - GM.get(g - 1)));
        }

        return G.get(0);
    }
}
